package com.renfei.multimediatask.ui.task8;

import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.media.AudioRecord;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.util.Size;
import android.view.SurfaceHolder;
import android.view.TextureView;
import android.view.View;
import android.widget.Button;
import android.widget.RelativeLayout;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;

import com.renfei.multimediatask.R;
import com.renfei.multimediatask.base.BaseActivity;
import com.renfei.multimediatask.camera.Camera2Utils;
import com.renfei.multimediatask.ui.task8.data.VideoData;
import com.renfei.multimediatask.util.Camera1Loader;
import com.renfei.multimediatask.util.Camera2Loader;
import com.renfei.multimediatask.util.CameraLoader;
import com.renfei.multimediatask.util.YUVDataUtil;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.util.concurrent.ArrayBlockingQueue;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;


public class VideoRecordActivity extends BaseActivity {


    @BindView(R.id.surfaceView)
    TextureView surfaceView;
    @BindView(R.id.startRecord)
    Button startRecord;
    @BindView(R.id.stopRecord)
    Button stopRecord;
    @BindView(R.id.ll_btn_layout)
    RelativeLayout llBtnLayout;
    private CameraLoader cameraLoader;


    private ArrayBlockingQueue<byte[]> queue;


    private String targetFilePath;

    private VideoEncodecThread videoEncodecThread;


    private int recordWidth;
    private int recordHeight;


    private int preViewWidth = 360;
    private int preViewHeight = 640;


    private boolean isEncodec;

    @Override
    protected int getLayoutId() {
        return R.layout.activity_video_record;
    }

    @Override
    protected void initListener() {
        queue = new ArrayBlockingQueue<>(1024);
        if (Build.VERSION.SDK_INT < 21) {
            cameraLoader = new Camera1Loader(this);
        } else {
            cameraLoader = new Camera2Loader(this);
        }


        cameraLoader.setOnPreviewFrameListener(new CameraLoader.OnPreviewFrameListener() {
            @Override
            public void onPreviewFrame(byte[] data, int width, int height) {
                //获取到NV21数据后，我们对其进行编码
//                Log.e("onPreviewFrame", data.length + "   width  =" + width + "  height  =" + height);

                if (null != videoEncodecThread && videoEncodecThread.isEncodec) {
                    try {
                        queue.put(data);
                    } catch (InterruptedException e) {
                        loge("onPreviewFrame", "queue put data exception");
                        e.printStackTrace();
                    }
                }
            }
        });
        surfaceView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
                loge("onSurfaceTextureAvailable", "width    =" + width);
                loge("onSurfaceTextureAvailable", "height    =" + height);
                cameraLoader.onCreate(preViewWidth, preViewHeight, new CameraLoader.OnPreviewSelectListener() {
                    @Override
                    public void onPreviewSizeSelect(int pwidth, int pheight) {
                        loge("onPreviewSizeSelect", "width    =" + pwidth);
                        loge("onPreviewSizeSelect", "height    =" + pheight);
                        recordWidth = pwidth;
                        recordHeight = pheight;

                        videoEncodecThread.initEncodec(recordWidth, recordHeight);
                    }
                });
                cameraLoader.startPreViewTextureView(surfaceTexture);


            }

            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

            }

            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
                return false;
            }

            @Override
            public void onSurfaceTextureUpdated(SurfaceTexture surface) {

            }
        });
        videoEncodecThread = new VideoEncodecThread(new WeakReference<>(this));
    }


    @OnClick({R.id.startRecord, R.id.stopRecord})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.startRecord:
                startRecording();
                break;
            case R.id.stopRecord:
                stopRecording();
                break;
        }
    }

    private void stopRecording() {
        if (null != videoEncodecThread) {
            videoEncodecThread.stopEncodec();
        }
    }

    private void startRecording() {
        if (null != videoEncodecThread && videoEncodecThread.isEncodec) {
            return;
        }
        videoEncodecThread.start();

    }


    @Override
    protected void onPause() {
        super.onPause();
        if (null != cameraLoader) {
            cameraLoader.onPause();
        }
    }


    //    static class VideoEncodecThread extends Thread {
//        private WeakReference<VideoRecordActivity> weakReference;
//
//        private static final String TAG = VideoEncodecThread.class.getSimpleName();
//
//
//        //是否正在编码
//        private boolean isEncodec = false;
//
//        private int width;
//        private int height;
//
//        //等待队列超时时间
//        private int TIMEOUT_USEC = 12000;
//
//        public VideoEncodecThread(WeakReference<VideoRecordActivity> weakReference) {
//            this.weakReference = weakReference;
//
//        }
//        public void initEncodec(int recordWidth, int recordHeight) {
//            this.width=recordWidth;
//            this.height=recordHeight;
//            weakReference.get().targetFilePath = weakReference.get().getParentFilePath() + "video_encodec_" + width + "_" + height + ".yuv";
//        }
//
//        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
//        @Override
//        public void run() {
//            super.run();
//
//            FileOutputStream fileOutputStream = null;
//            try {
//                File file = new File(weakReference.get().targetFilePath);
//                if (file.exists()) {
//                    file.delete();
//                }
//                file.createNewFile();
//
//                fileOutputStream = new FileOutputStream(file);
//
//                isEncodec = true;
//                byte[] input = null;
//
//                int offset=0;
//
//                while (isEncodec) {
//                    //处理输入
//                    if (weakReference.get().queue.size() > 0) {
//                        input = weakReference.get().queue.poll();
//
//                        byte[] yuv420sp = new byte[width * height * 3 / 2];
//                        YUVDataUtil.NV21ToNV12(input, yuv420sp, width, height);
//                        input = yuv420sp;
//                    }
//                    if (null != input) {
//                        //写入到文件
//                        fileOutputStream.write(input);
//                        Log.e(TAG, "fileOutputStream.write: "+input.length);
////                        offset=offset+input.length;
//                    }
//                }
//
//                isEncodec = false;
//
//                fileOutputStream.flush();
//                fileOutputStream.close();
//
//                Log.e(TAG, "videoRecordActivity:  录制完成");
//            } catch (IOException e) {
//                e.printStackTrace();
//            }
//        }
//
//
//        public void stopEncodec() {
//            isEncodec = false;
//        }
//
//
//    }
    static class VideoEncodecThread extends Thread {
        private WeakReference<VideoRecordActivity> weakReference;

        private static final String TAG = VideoEncodecThread.class.getSimpleName();

        //视频编码
        private MediaCodec videoEncodec;
        private MediaFormat videoFormat;


        //是否正在编码
        private boolean isEncodec = false;

        private int width;
        private int height;

        //等待队列超时时间
        private int TIMEOUT_USEC = 12000;


        public VideoEncodecThread(WeakReference<VideoRecordActivity> weakReference) {
            this.weakReference = weakReference;

        }

        public void initEncodec(int width, int height) {
            Log.e(TAG, "init Encodec   width    =" + width + "  height  =" + height);
            this.width = width;
            this.height = height;
            initEncodec();
        }


        private void initEncodec() {
            try {

                weakReference.get().targetFilePath = weakReference.get().getParentFilePath() + "video_encodec.h264";

                videoFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height);
                videoFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);
                videoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 25);  //每秒30帧
                videoFormat.setInteger(MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR);
                videoFormat.setInteger(MediaFormat.KEY_BIT_RATE, width * height * 5);
                videoFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);//设置关键帧

                videoEncodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
                videoEncodec.configure(videoFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
                videoEncodec.start();

            } catch (IOException e) {
                e.printStackTrace();
            }
        }


        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        @Override
        public void run() {
            super.run();

            FileOutputStream fileOutputStream = null;
            try {
                File file = new File(weakReference.get().targetFilePath);
                if (file.exists()) {
                    file.delete();
                }
                file.createNewFile();
                fileOutputStream = new FileOutputStream(file);


                isEncodec = true;
                byte[] inputData = null;

//                long pts =  0;
//                long frameCount = 0;


                while (isEncodec) {
                    //处理输入
                    if (weakReference.get().queue.size() > 0) {
                        inputData = weakReference.get().queue.poll();
                        byte[] yuv420sp = new byte[width * height * 3 / 2];
                        YUVDataUtil.NV21ToNV12(inputData, yuv420sp, width, height);
                        inputData = yuv420sp;
                    }
                    if (null != inputData) {
                        int inputBufferIndex = videoEncodec.dequeueInputBuffer(-1);
                        if (inputBufferIndex >= 0) {
//                            pts = computePresentationTime(frameCount);//转成为妙

                            ByteBuffer byteBuffer = videoEncodec.getInputBuffer(inputBufferIndex);
                            byteBuffer.clear();
                            byteBuffer.put(inputData, 0, inputData.length);


                            videoEncodec.queueInputBuffer(inputBufferIndex, 0,
                                    inputData.length,
                                    getPTSUs(), 0
                            );
//                            frameCount+=1;
                        }
                    }
                    //处理输出
                    MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
                    int outputBufferIndex = videoEncodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC);

                    while (outputBufferIndex >= 0) {
                        ByteBuffer outputBuffer = videoEncodec.getOutputBuffer(outputBufferIndex);
                        //取出数据
                        byte[] outData = new byte[bufferInfo.size];
                        outputBuffer.get(outData);

                        // flags 利用位操作，定义的 flag 都是 2 的倍数
                        if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { // 配置相关的内容，也就是 SPS，PPS
                            fileOutputStream.write(outData, 0, outData.length);
                        } else if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) { // 关键帧
                            fileOutputStream.write(outData, 0, outData.length);
                        } else {
                            // 非关键帧和SPS、PPS,直接写入文件，可能是B帧或者P帧
                            fileOutputStream.write(outData, 0, outData.length);
                        }

                        videoEncodec.releaseOutputBuffer(outputBufferIndex, false);
                        outputBufferIndex = videoEncodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC);
                    }
                }

                isEncodec = false;

                videoEncodec.stop();
                videoEncodec.release();

                Log.e(TAG, "videoRecordActivity:  录制完成");
            } catch (IOException e) {
                e.printStackTrace();
                release();
            }
        }

        /**
         * previous presentationTimeUs for writing
         */
        private long prevOutputPTSUs = 0;

        /**
         * get next encoding presentationTimeUs
         *
         * @return
         */
        protected long getPTSUs() {
            long result = System.nanoTime() / 1000L;
            if (result < prevOutputPTSUs)
                result = (prevOutputPTSUs - result) + result;
            return result;
        }


        public void stopEncodec() {
            isEncodec = false;
        }

        public void release() {

            Log.e(TAG, "videoRecordActivity:  release");
            if (null != videoEncodec) {

                videoEncodec.stop();
                videoEncodec.release();
                videoEncodec = null;
            }
        }
    }
}
